﻿using ComposantReferentiel.Champ;
using ComposantReferentiel.RapportErreur;
using MessageBox;
using ReadAndWrite;
using GeneralServices.PatternBuilder;
using GeneralServices.ViewModel;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Data.Entity.Validation;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Controls;
using System.Windows.Input;

namespace ComposantReferentiel
{
    public class ReferentielControlVM : ViewModelBase
    {
        #region Attribut(s)

        private DbSet dataSource;
        private bool noBlock;
        private MODE_COMPOSANT_REFERENTIIEL mode;

        #endregion

        #region Propriété(s)

        #region Command

        public ICommand FonctionAdd
        {
            get;
            private set;
        }

        public ICommand FonctionErase
        {
            get;
            private set;
        }

        public ICommand Valid
        {
            get;
            private set;
        }

        public ICommand Cancel
        {
            get;
            private set;
        }

        public ICommand ExportCSV
        {
            get;
            private set;
        }

        public ICommand ExportPDF
        {
            get;
            private set;
        }

        public ICommand Close
        {
            get;
            private set;
        }

        #endregion

        public DbSet DataSource
        {
            get
            {
                return this.dataSource;
            }
            set
            {
                this.dataSource = value;
                this.OnPropertyChanged("DataSource");
                this.OnPropertyChanged("ItemsSource");
            }
        }

        public IList ItemsSource
        {
            get
            {
                IList result;
                if (this.DataSource != null)
                {
                    result = this.DataSource.Local;
                }
                else
                {
                    result = null;
                }
                return result;
            }
        }

        public bool NoBlock
        {
            get
            {
                return this.noBlock;
            }
            set
            {
                this.noBlock = value;
                this.OnPropertyChanged("NoBlock");
            }
        }

        public MODE_COMPOSANT_REFERENTIIEL Mode
        {
            get
            {
                return this.mode;
            }
            set
            {
                this.mode = value;
                this.OnPropertyChanged("Mode");
                if (this.ControlChamps != null)
                {
                    this.ControlChamps.Mode = this.mode;
                }
            }
        }

        public ControlChamps ControlChamps
        {
            get;
            set;
        }

        #endregion

        #region Constructeur(s)

        public ReferentielControlVM()
        {
            this.NoBlock = true;
            this.FonctionAdd = new RelayCommand(new Action<object>(this.FonctionAddDelegate), new Func<object, bool>(this.CanFonctionAddDelegate));
            this.FonctionErase = new RelayCommand(new Action<object>(this.FonctionEraseDelegate), new Func<object, bool>(this.CanFonctionEraseDelegate));
            this.Valid = new RelayCommand(new Action<object>(this.ValidDelegate));
            this.Cancel = new RelayCommand(new Action<object>(this.CancelDelegate));
            this.Close = new RelayCommand(new Action<object>(this.CloseDelegate));
            this.ExportCSV = new RelayCommand(new Action<object>(this.ExportCSVDelegate));
            this.ExportPDF = new RelayCommand(new Action<object>(this.ExportPDFDelegate));
        }

        #endregion

        #region Méthode(s)

        #region Event

        /// <summary>
        /// Export PDF de la grille de données.
        /// </summary>
        /// <param name="obj"></param>
        private void ExportPDFDelegate(object obj)
        {
        }

        /// <summary>
        /// Export CSV de la grille de données.
        /// </summary>
        /// <param name="obj"></param>
        private void ExportCSVDelegate(object obj)
        {
            ReferentielControl _ref = (obj as ReferentielControl);
            DataGrid dataGrid = _ref != null ? _ref.dg : null;

            if (dataGrid != null)
            {
                ParamExportCSV p = _ref.Observer.GetParam();
                ReadAndWriteCSV readAndWriteCSV = new ReadAndWriteCSV(p.Path, p.Separateur);
                List<ISerializableCSV> list = new List<ISerializableCSV>(dataGrid.ItemsSource.Cast<ISerializableCSV>());
                readAndWriteCSV.WriteCSV(p.GetHeaderBody(), list);
            }
        }

        /// <summary>
        /// Annuler les modifications sur la grille de données.
        /// </summary>
        /// <param name="obj"></param>
        private void CancelDelegate(object obj)
        {
            ReferentielControl referentielControl = obj as ReferentielControl;
            if (referentielControl != null)
            {
                referentielControl.LoadData("RELOAD");
            }
        }


        /// <summary>
        /// Valider les modifications sur la grille de données.
        /// Peut générer un rapport d'erreur s'il y a des problèmes d'insertion en base de données.
        /// FK , PK, ou autres contraintes.
        /// </summary>
        /// <param name="obj"></param>
        private void ValidDelegate(object obj)
        {
            ReferentielControl referentielControl = obj as ReferentielControl;
            IBuilder builder = null;
            if (referentielControl != null)
            {
                try
                {
                    referentielControl.SaveChange();
                }
                catch (DbEntityValidationException)
                {
                    List<DbEntityValidationResult> list = referentielControl.Context.GetValidationErrors().ToList<DbEntityValidationResult>();
                    if (list.Count != 0)
                    {
                        builder = new BuilderRapportValidation();
                        builder.BuildPart(list);
                    }
                }
                catch (DbUpdateException ex)
                {
                    builder = new BuilderRapportOracleException();
                    builder.BuildPart(ex);
                }
                finally
                {
                    if (builder != null)
                    {
                        RapportErreurView rapport = new RapportErreurView(builder.GetResult().ConvertAll<Rapport>((IBuilt input) => input as Rapport))
                        {
                            BasPage = "Voulez-vous annuler les modifications ?"
                        };
                        rapport.ShowDialog();

                        if (rapport.Refresh)
                            this.CancelDelegate(obj);
                    }
                }
            }
        }

        private bool CanFonctionAddDelegate(object arg)
        {
            return true;
        }

        /// <summary>
        /// Permet d'ajouter un enregistrement à la grille de données.
        /// Dans un premier temps, la grille est vérouillé tant que l'utilisateur ne valide pas la saisie de ce nouvel enregistrement.
        /// </summary>
        /// <param name="obj"></param>
        private void FonctionAddDelegate(object obj)
        {
            bool flag = true;
            if (this.Mode == MODE_COMPOSANT_REFERENTIIEL.CONSULT)
            {
                ReferentielControl referentielControl = obj as ReferentielControl;
                object obj2 = Activator.CreateInstance(referentielControl.TypeData, false);

                // L'observer est appelé pour mettre à jour les champs que le développeur souhaite
                // Ex : Champs de traca (Date heure creation, modif, ...)
                if (referentielControl.Observer != null)
                    referentielControl.Observer.AfterCreate(ref obj2);
                
                this.DataSource.Add(obj2);
                referentielControl.dg.SelectedItem = obj2;
            }
            else
            {
                if (this.Mode == MODE_COMPOSANT_REFERENTIIEL.ADD)
                {
                    foreach (ChampGenerique current in this.ControlChamps.CollectionChamps)
                    {
                        if (!current.IsValid)
                        {
                            flag = false;
                            break;
                        }
                    }
                    if (!flag)
                    {
                        ShowMessageBox.Show(TYPE_WINDOW_BOX.AVERTISSEMENT, "", "La validation a échoué. Certains champs sont invalides par rapport aux contraintes définies dans la configuration.");
                    }
                }
            }
            this.Mode = (flag ? ((this.Mode == MODE_COMPOSANT_REFERENTIIEL.CONSULT) ? MODE_COMPOSANT_REFERENTIIEL.ADD : MODE_COMPOSANT_REFERENTIIEL.CONSULT) : this.Mode);
        }

        private bool CanFonctionEraseDelegate(object arg)
        {
            return this.mode != MODE_COMPOSANT_REFERENTIIEL.ADD && arg is DataGrid && (arg as DataGrid).SelectedItem != null;
        }

        /// <summary>
        /// Permet de supprimer un enregistrement de la grille de données.
        /// </summary>
        /// <param name="obj"></param>
        private void FonctionEraseDelegate(object obj)
        {
            this.Mode = MODE_COMPOSANT_REFERENTIIEL.ERASE;
            DataGrid dataGrid = obj as DataGrid;
            this.DataSource.Remove(dataGrid.SelectedItem);
            this.Mode = MODE_COMPOSANT_REFERENTIIEL.CONSULT;
        }

        /// <summary>
        /// Permet de femer le composant référentiel.
        /// </summary>
        /// <param name="obj"></param>
        private void CloseDelegate(object obj)
        {
            if (ShowMessageBox.Show(
                TYPE_WINDOW_BOX.CONFIRM,
                @"Vous allez fermer cet écran référentiel. Toutes les modifications non enregistrées seront perdues.",
                @"") == TYPE_RESULT_BOX.OK)
            {
                ReferentielControl _ref = obj as ReferentielControl;
                if (_ref != null)
                    _ref.Observer.CloseReferentielComposant();
            }
        }

        #endregion

        #endregion
    }
}
